/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/** packages that contains the System Classes as the Core.*/
package org.easyway.system;

import java.awt.Container;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import org.easyway.annotations.editor.Method;
import org.easyway.annotations.editor.Resource;
import org.easyway.annotations.editor.UEditor;
import org.easyway.collisions.CoreCollision;
import org.easyway.debug.DebugManager;
import org.easyway.editor.commands.CommandList;
import org.easyway.input.Mouse;
import org.easyway.interfaces.base.ITexture;
import org.easyway.interfaces.extended.ILoopable;
import org.easyway.interfaces.extended.IRender;
import org.easyway.lists.CollisionableLoopList;
import org.easyway.lists.DrawingLayaredList;
import org.easyway.lists.LoopList;
import org.easyway.objects.BaseObject;
import org.easyway.objects.Camera;
import org.easyway.objects.animo.AnimoList;
import org.easyway.sounds.OggPlayer;
import org.easyway.utils.Utility;
import org.lwjgl.LWJGLException;
import org.lwjgl.devil.IL;
import org.lwjgl.devil.ILU;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.AWTInputAdapter;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

/**
 * The main class: extends your project from this<br>
 * This is an abstract class that implements IDrawing and ILoopable interfaces<br>
 * you MUST override the methods:<br>
 * public void loop();<br>
 * public void render();<br>
 * public void creation();<br>
 * <br>
 * <br>
 * <b>Example:</b> <br>
 * <font color=GREE>// import the class Core</font><br>
 * <b>import</b> org.easyway.system.Core;<br>
 * <br>
 * <b>public</b> Game <b>extends</b> Core {<br>
 * <br>
 * <b>public</b> Game() {<br>
 * <font color=GREEN>// width = 1024, height = 768, fullscreen = true</font><br>
 * <b>super</b>( 1024, 768, true );<br> }<br>
 * <br>
 * <b>public void</b> creation() {<br>
 * <font color=GREEN>// insert here your initialization code</font><br> }<br>
 * <br>
 * <b>public void</b> loop() {<br>
 * <font color=GREEN>// insert here your data-loop code</font><br> }<br>
 * <br>
 * <b>public void</b> render() {<br>
 * <font color=GREEN>// insert here your (opengl) rendering code</font><br>
 * <font color=GREEN>// this method is usually not used</font><br> }<br>
 * <br>
 * <b>public static</b> main( String args[] ) {<br>
 * <font color=GREEN>// creates a new instance of the Game</font><br>
 * <b>new</b> Game();<br>
 * <font color=GREEN>// Caution: the initialization code MUST written in
 * creation Method</font><br>
 * <font color=GREEN>// and NOT in the 'main' method</font><br> }<br>
 * <br> }<br>
 * 
 * @author Daniele Paggi
 * @version 1
 */
@UEditor("System")//
public abstract class Core extends BaseObject implements IRender, ILoopable {

	/**
	 * the game engine version
	 */
	public static final String VERSION = "0.3.5.0";

	/** Thread that manages the game */
	private Sincro sincro;

	/** width, height and bit per pixel */
	private int width, height, bpp;

	/** is fullscreen or windowed? */
	private boolean fullscreen;

	/** indicates if the game is in pause */
	private boolean paused;

	/** the contsiner that contains the OpenGL Window */
	protected Container container;

	/** the internal OpenGL window */
	public InternalWindow internalWindow;

	/**
	 * game engine's camera
	 * 
	 * @see org.easyway.objects.Camera
	 */
	public Camera camera;

	/**
	 * beta: use a multithread Core?<br>
	 * <font color=RED>cution:</font><br>
	 * not tested and supported yet
	 */
	public static boolean multiThread;

	public static boolean autoInitLibs = true;

	public static boolean useLwjgl;

	public final static int numberOfLayers = 7;

	/** indicates if the Game Engine is already initialized */
	public boolean initialized = false;

	/**
	 * create a new instance of core
	 * 
	 * @param width
	 *            the width of screen
	 * @param height
	 *            the height of screen
	 * @param fullscreen
	 *            fullscreen or window mode?
	 */
	public Core(int width, int height, boolean fullscreen) {
		this(width, height, 32, fullscreen, false);
	}

	/**
	 * create a new instance of core
	 * 
	 * @param width
	 *            the width of screen
	 * @param height
	 *            the height of screen
	 * @param bpp
	 *            bit per pixel *
	 * @param fullscreen
	 *            fullscreen or window mode?
	 */
	public Core(int width, int height, int bpp, boolean fullscreen) {
		this(width, height, bpp, fullscreen, false);
	}

	/**
	 * create a new instance of core
	 * 
	 * @param width
	 *            the width of screen
	 * @param height
	 *            the height of screen
	 * @param bpp
	 *            bit per pixel
	 */
	public Core(int width, int height, int bpp, boolean fullscreen,
			boolean multiThread) {
		useLwjgl = true;
		if (autoInitLibs)
			setLibrary();
		StaticRef.core = this;
		Core.multiThread = multiThread;
		if (multiThread) {
			sincro = new ASincroL();
		} else {
			sincro = new Sincro();
		}
		this.width = width;
		this.height = height;
		this.bpp = bpp;
		this.fullscreen = fullscreen;
		initCoreData();
		sincro.start();
	}

	/**
	 * create a new instance of core
	 * 
	 * @param width
	 *            the width of screen
	 * @param height
	 *            the height of screen
	 * @param bpp
	 *            bit per pixel
	 */
	public Core(int width, int height, int bpp, Container container) {
		useLwjgl = false;
		if (autoInitLibs)
			setLibrary();
		StaticRef.core = this;
		Core.multiThread = false;
		this.container = container;
		// if (multiThread) {
		// sincro = new ASincroL();
		// } else {
		sincro = new Sincro();
		// }
		this.width = width;
		this.height = height;
		this.bpp = bpp;
		this.fullscreen = false;
		initCoreData();
		sincro.start();
	}

	protected void initCoreData() {
		StaticRef.initialized = false;
		StaticRef.loopList = new LoopList();
		// TODO: for defaults we adds 7 drawing list
		// we can customize this decision?
		StaticRef.layers = new DrawingLayaredList[numberOfLayers];
		for (int i = 0; i < numberOfLayers; ++i)
			StaticRef.layers[i] = new DrawingLayaredList();

		StaticRef.collisionableLoopList = new CollisionableLoopList();
		StaticRef.animoList = new AnimoList();// new Vector<Animo>();
		StaticRef.textures = new ArrayList<ITexture>(50);
		camera = StaticRef.camera = new Camera(0, 0, width, height);
		StaticRef.coreCollision = new CoreCollision();
		StaticRef.initialized = true;

	}

	/** initializes the Game Engine */
	protected void init() {
		System.out.println("\nEasyWay Game Engine V. " + VERSION
				+ "\nhttp://easyway.sf.net\n");
		OggPlayer.init();
		try {
			IL.create();
			ILU.create();
		} catch (Exception e) {
			Utility.error("can't create DevIL", "Core.init()", e);
		}
		if (container == null) {
			createWindow();

			try {
				Keyboard.create();
				org.easyway.input.Keyboard.create();
				org.lwjgl.input.Mouse.create();
				StaticRef.camera.initGL();
				// org.lwjgl.input.Mouse.
				// Mouse.enableBuffer(); // old
			} catch (LWJGLException e) {
				Utility.error("Error creating the Keyboard or the Mouse",
						"Core.init()", e);
			}
		} else {

			try {
				org.easyway.input.Keyboard.create();
				System.out.print("Making internalWindow: ");
				internalWindow = new InternalWindow();
				// internalWindow.setSize(width, height);
				container.add(internalWindow);
				internalWindow.setSize(container.getSize());
				AWTInputAdapter.create(internalWindow);
			} catch (LWJGLException e) {
				Utility.error("Error creating the InternalWindow",
						"Core.init()", e);
			}
			System.out.print("Done");
		}

		initialized = true;
	}

	// --------------------------------------------------------------------------------
	/** creates a new window */
	private void createWindow() {
		try {
			if (findDisplayMode() == null) {
				Utility
						.error(
								"Can't find a correct OpenGL window configuration: try to change the game's resolution\n",
								"Core.createWindow");
				if (bpp == 32) {
					Utility
							.error(
									"You have selected 32bpp: I'll try to downgrade to 24bpp.. please wait",
									"Core.createWindow");

					bpp = 24;
					if (findDisplayMode() == null) {
						System.exit(-1); // error
					}
				} else {
					System.exit(-1); // error
				}
			}
			Display.create();
			Display.setFullscreen(fullscreen);

			// TODO: setVSyncEnabled( true\false ) selectable by the user of
			// Game Engine
			Display.setVSyncEnabled(true);
			Display.makeCurrent();
			Display.setTitle("EasyWay Game Engine (by Paggi Daniele)");// default
			// title
		} catch (LWJGLException e) {
			Utility.error("Can't init OpenGL: update your graphics drivers",
					"Core.createWindow", e);
			System.exit(-1);
		}
	}

	/** finds and 'set' the display mode selected */
	private DisplayMode findDisplayMode() {
		DisplayMode[] modes;
		try {
			modes = Display.getAvailableDisplayModes();
		} catch (LWJGLException e) {
			e.printStackTrace();
			return null;
		}
		for (int i = 0; i < modes.length; i++) {
			if (modes[i].getWidth() == width && modes[i].getHeight() == height
					&& modes[i].getBitsPerPixel() >= bpp
					&& modes[i].getFrequency() <= 85) { // XXX: getFrequency <=
				// 85 it's correct?
				try {
					Display.setDisplayMode(modes[i]);
				} catch (LWJGLException e) {
					e.printStackTrace();
				}
				return modes[i];
			}
		}
		return null;
	}

	// --------------------------------------------------------------------------------
	/** cleanup the Engine */
	protected void cleanup() {
		// TODO
		// OggPlayer.destroy();
		AWTInputAdapter.destroy();
		Keyboard.destroy();
		org.lwjgl.input.Mouse.destroy();
		if (useLwjgl) {
			Display.destroy();
		}
		StaticRef.music.release();
		// StaticRef.music.destroy();
		OggPlayer.destroy();
		// StaticRef.core = null;
		// System.exit(0);
		System.out.println("Game Engine Cleaned Up");
		setTimeOut(0);

		// force to close all
		System.exit(0);
	}

	/**
	 * sets the window's title
	 * 
	 * @param title
	 *            the new title of game window
	 */
	public void setTitle(String title) {
		Display.setTitle(title);
	}

	/** data loop */
	protected void coreLoop() {
		// if (useLwjgl) {

		if (Keyboard.isCreated()) {
			org.easyway.input.Keyboard.loop();
			Keyboard.poll();
			while (Keyboard.next()) {
				if (Keyboard.getEventKey() != Keyboard.KEY_NONE)
					org.easyway.input.Keyboard
							.sendEvent(Keyboard.getEventKey(), Keyboard
									.getEventKeyState());
			}
		}
		if (org.lwjgl.input.Mouse.isCreated())
			org.lwjgl.input.Mouse.poll();
		// engine's mouse loop
		// }
		Mouse.loop();
		StaticRef.getCamera().objectsAtScreen.clear();
		if (DebugManager.debug) {
			CommandList.loop();
			return;
		}

		// animoList is always different from null
		assert StaticRef.animoList != null;
		// Animo temp;
		// for (int i = StaticRef.animoList.size() - 1; i >= 0; --i) {
		// temp = (Animo) StaticRef.animoList.get(i);
		// if (temp != null)
		// temp.loop();
		// }
		loop();
		StaticRef.animoList.loop();
		StaticRef.loopList.loop();

		// StaticRef.collisionableLoopList.resetCollisioni(); // old
		// checks collision
		StaticRef.coreCollision.loop(); // test collision
		StaticRef.collisionableLoopList.loop(); // calls "onCollision" methods
		camera.center(); // center the camera if it's attracked
	}

	/** drawing loop */
	protected void coreRender() {
		// -ita- sicuri che il glClear sia da tenere SEMPRE cosi'?
		// we can get the possibilty to change or ignore this passage?
		// for example:
		// if (clearMask!=0) {
		// GL11.glClear( clearMask );
		// }
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		GL11.glLoadIdentity();
		assert StaticRef.layers != null;
		synchronized (StaticRef.layers) {
			// StaticRef.drawingList.render();
			for (int i = 0; i < StaticRef.layers.length; ++i) {
				if (StaticRef.layers[i] != null)
					StaticRef.layers[i].render();
			}
		}
		render();
		// Mouse.updateCursor();
	}

	/**
	 * initializes the resources<br>
	 * this method must be oveerided<br>
	 */
	public abstract void creation();

	// --------------------------------------------------------------------------------
	// -------------------------------USER_METHODS-------------------------------------
	// --------------------------------------------------------------------------------
	/**
	 * destroys all game objects<br>
	 * CAUTION: this will set the layers to the default configuration!<br>
	 * <br>
	 * code:<br>
	 * for (int i = 0; i < StaticRef.layers.length; ++i) {<br>
	 * if (StaticRef.layers[i] != null) {<br>
	 * StaticRef.layers[i] = new DrawingLayaredList();<br> }<br> }<br>
	 * StaticRef.loopList = new LoopList();<br>
	 */
	public void destroyAllObject() {
		for (int i = 0; i < StaticRef.layers.length; ++i) {
			if (StaticRef.layers[i] != null) {
				StaticRef.layers[i] = new DrawingLayaredList();
			}
		}
		StaticRef.loopList = new LoopList();
	}

	public void destroy() {
		if (isDestroyed())
			return;
		super.destroy();
		endGame();
	}

	/** close the game */
	public void endGame() {
		Sincro.loop = false;
	}

	// TODO: Contrallare il commento
	/**
	 * frozes the game<br>
	 * 
	 * @see resumeGame()
	 * @see isGamePaused()
	 */
	public void pauseGame() {
		paused = true;
	}

	/**
	 * sets the game to the normal status <br>
	 * 
	 * @see pauseGame()
	 * @see isGamePaused()
	 */
	public void resumeGame() {
		paused = false;
	}

	/**
	 * returns if the game is paused or not
	 * 
	 * @see pauseGame()
	 * @see resumeGame()
	 */
	public boolean isGamePaused() {
		return paused;
	}

	/** switch from FullScreen mode to window mode and viceversa */
	public void switchMode() {
		fullscreen = !fullscreen;
		try {
			Display.setFullscreen(fullscreen);
		} catch (LWJGLException e) {
			Utility.error(" fullscreen value: " + fullscreen, e);
		}
	}

	/**
	 * enable or disable the VSync<br>
	 * disabling the VSync you'll get a speed up in your game
	 * 
	 * @param value
	 */
	public void setVSync(boolean value) {
		Display.setVSyncEnabled(value);
	}

	/**
	 * sets the fps of game<br>
	 * the default value is 60<br>
	 * 
	 * @param fps
	 *            frame per second
	 * @see getFps()
	 */
	public void setFps(int fps) {
		Sincro.fps = fps;
	}

	/**
	 * gets the fps of game
	 * 
	 * @return the current frame per second that the game has done.
	 * @see setFps()
	 */
	public int getFps() {
		return sincro.getFps();
	}

	// TODO: reviews this commet
	// -ita- rivedere il commento
	/**
	 * sets the time of timeOutOperation.<br>
	 * if time is less than 1000, the timeout will be disabled.<br>
	 * I should you sets this to 0 when you are going to make your final
	 * distribution<br>
	 * 
	 * @param time
	 *            time in ms
	 */
	public void setTimeOut(int time) {
		//sincro.timeOut = time;
		sincro.setTimeOut(time);
	}

	/**
	 * returns if the game is in fullscreen mode or not
	 * 
	 * @return is the game in fullscreen mode?
	 */
	public boolean isFullscreen() {
		return fullscreen;
	}

	/**
	 * returns the time of timeOutOperation.<br>
	 * if time is less than 1000, the timeout will be disabled.<br>
	 * I should you sets this to 0 when you are going to make your final
	 * distribution<br>
	 * 
	 * @return the time in ms
	 */
	public int getTimeOut() {
		return sincro.timeOut;
	}

	/**
	 * returns the game size
	 * 
	 * @return game window's Width
	 * @see getHeight()
	 * @see getBpp()
	 */
	public int getWidth() {
		return width;
	}

	/**
	 * returns the game size
	 * 
	 * @return game window's Height
	 * @see getWidth()
	 * @see getBpp()
	 */
	public int getHeight() {
		return height;
	}

	/**
	 * returns the game bpp<br>
	 * 
	 * @return game bit per pixel
	 * @see getWidth()
	 * @see getHeight()
	 */
	public int getBpp() {
		return bpp;
	}

	/**
	 * sets if enable or not the kill message<br>
	 * if the kill message is enabled the onCollision method will be not
	 * executed.
	 * 
	 * @param enable
	 *            indicates if the kill message is to enable or not
	 */
	public void setKillMessage(boolean enable) { // 0.1.6
		BaseObject.KILL = enable;
	}

	// TODO:
	/**
	 * saves current game state - not completed at 100%
	 * 
	 * @see load(String)
	 */
	@Method("save world")//
	public synchronized static void save(@Resource("output file") String file) {
		System.out.println();
		System.out.println();
		System.out.println("GAME SAVING...as: " + file);
		try {
			FileOutputStream fout = new FileOutputStream(file);
			ObjectOutputStream out = new ObjectOutputStream(fout);
			// out.writeObject(StaticRef.drawingList);
			out.writeObject(StaticRef.layers);
			out.writeObject(StaticRef.loopList);
			out.writeObject(StaticRef.animoList);
			// out.writeObject(StaticRef.collisionableLoopList);
			out.writeObject(StaticRef.coreCollision);
			out.writeObject(StaticRef.camera);
			// if (DebugManager.debug) {
			// out.writeBoolean(true);
			// out.writeObject(TileManagerEditor.tiles);
			// }
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("..End Saving");
		}
		System.out.println();
		System.out.println();
	}

	/**
	 * loads current game state
	 * 
	 * @see save(String)
	 */
	// @SuppressWarnings("unchecked")//
	@Method("load incremental world")//
	public synchronized static void loadIncremental(
			@Resource("input file") String file) {
		System.out.println();
		System.out.println();
		System.out.println("GAME LOADING (incremental mode)...");
		try {
			FileInputStream fin = new FileInputStream(file);
			ObjectInputStream ois = new ObjectInputStream(fin);
			
			DrawingLayaredList temp[] = (DrawingLayaredList[]) ois.readObject();
			if (temp.length > StaticRef.layers.length) {
				// switch:
				DrawingLayaredList switcher[] = temp;
				temp = StaticRef.layers;
				StaticRef.layers = switcher;
			}
			for (int i = 0; i < temp.length; ++i)
				StaticRef.layers[i].addElementsFrom(temp[i]);
			
			StaticRef.loopList.addElementsFrom( (LoopList) ois.readObject() );
			
			/* -- unchecked warning -- */
			StaticRef.animoList.addElementsFrom( (AnimoList) ois.readObject() );
			// StaticRef.collisionableLoopList = (CollisionableLoopList)
			// ois.readObject();
			StaticRef.coreCollision.loadIncremental( (CoreCollision) ois.readObject() );
			StaticRef.core.camera = StaticRef.camera = (Camera) ois
					.readObject();
			// if (ois.readBoolean()) {
			// TileManagerEditor.tiles = (ArrayList<Tile>) ois.readObject();
			// }

			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("..End Loading");
		}
		System.out.println();
		System.out.println();
	}

	/**
	 * loads current game state
	 * 
	 * @see save(String)
	 */
	@SuppressWarnings("unchecked")//
	@Method("load world")//
	public synchronized static void load(@Resource("input file") String file) {
		System.out.println();
		System.out.println();
		System.out.println("GAME LOADING...");
		try {
			FileInputStream fin = new FileInputStream(file);
			ObjectInputStream ois = new ObjectInputStream(fin);
			// StaticRef.drawingList = (DrawingList) ois.readObject();
			StaticRef.layers = (DrawingLayaredList[]) ois.readObject();
			StaticRef.loopList = (LoopList) ois.readObject();
			/* -- unchecked warning -- */
			StaticRef.animoList = (AnimoList) ois.readObject();
			// StaticRef.collisionableLoopList = (CollisionableLoopList)
			// ois.readObject();
			StaticRef.coreCollision = (CoreCollision) ois.readObject();
			StaticRef.core.camera = StaticRef.camera = (Camera) ois
					.readObject();
			// if (ois.readBoolean()) {
			// TileManagerEditor.tiles = (ArrayList<Tile>) ois.readObject();
			// }

			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("..End Loading");
		}
		System.out.println();
		System.out.println();
	}

	/** returns the elaspedTime from the last loop */
	public long getElaspedTime() {
		return sincro.getElaspedTime();
	}

	public String toString() {
		return "Core <width: " + width + " Height: " + height + " bpp: " + bpp
				+ " initialized: " + initialized + " >";
	}

	static private int os = -1;

	/**
	 * 
	 * @return 0: windows, 1: linux, 2: mac
	 */
	public static int getOS() {
		if (os == -1) {
			String osName = System.getProperty("os.name");
			if (osName.startsWith("Windows")) {
				os = 0;
			} else if (osName.startsWith("Linux")
					|| osName.startsWith("FreeBDS")
					|| osName.startsWith("SunOS")) {
				os = 1;
			} else if (osName.startsWith("Mac OS X")) {
				os = 2;
			} else {
				throw new LinkageError("Unknown platform: " + osName);
			}
		}
		return os;
	}

	public static void setLibrary() {
		String dir;
		String osName = System.getProperty("os.name");
		String path = "";
		ArrayList<String> libs = new ArrayList<String>(10);

		try {
			// dir = Core.class.getClassLoader().getResource("").getPath();
			dir = Core.class.getResource("Core.class").getPath();
			dir = dir.substring(0, dir.indexOf("org"));
			// dir = dir.substring(0, dir.length() - 4);
		} catch (NullPointerException e) {
			// if the game is inside an jar file
			// TODO check if path correct
			// dir = Core.class.getResource("Core.class").getPath();
			// dir = dir.substring(0, dir.indexOf("!"));
			// dir = dir.substring(0, dir.lastIndexOf("/") + 1);
			dir = Core.class.getResource("Core.class").getPath();
			dir = dir.substring(0, dir.indexOf("org"));
			System.out
					.println("NullPointerException captured in Core.setLibrary(); dir: "
							+ dir);
		}

		System.out.println(dir);
		if (dir.indexOf("file:/") != -1) {
			dir = dir.substring(5);
		}

		if (osName.startsWith("Windows")) {
			path = "lib/win";
			libs.add("ILU.dll");
			libs.add("jinput-dx8.dll");
			libs.add("lwjgl.dll");
			libs.add("lwjgl-fmod3.dll");
			libs.add("DevIL.dll");
			libs.add("ILUT.dll");
			libs.add("jinput-raw.dll");
			libs.add("lwjgl-devil.dll");
			libs.add("OpenAL32.dll");
			dir = dir.substring(1); // can't handle starting "/"
		} else if (osName.startsWith("Linux") || osName.startsWith("FreeBDS")
				|| osName.startsWith("SunOS")) {
			path = "lib/linux";
			libs.add("libILU.so");
			libs.add("libjinput-linux.so");
			libs.add("liblwjgl.so");
			libs.add("liblwjgl-fmod3.so");
			libs.add("libIL.so");
			libs.add("libILUT.so");
			libs.add("liblwjgl64.so");
			libs.add("liblwjgl-devil.so");
			libs.add("libopenal.so");
		} else if (osName.startsWith("Mac OS X")) {
			path = "lib/mac";
			libs.add("libILU.dylib");
			libs.add("libjinput-osx.jnilib");
			libs.add("liblwjgl-devil.jnilib");
			libs.add("openal.dylib");
			libs.add("libIL.dylib");
			libs.add("libILUT.dylib");
			libs.add("liblwjgl.jnilib");
			libs.add("liblwjgl-fmod3.jnilib");
		} else {
			throw new LinkageError("Unknown platform: " + osName);
		}
		dir = dir.replace("%20", " ");
		try {
			System.setProperty("org.lwjgl.librarypath", dir + path);
			System.setProperty("java.library.path", dir + path);
			Utility.error("Using driver location: " + dir + path,
					"Core.setLibrary()");
			// System.loadLibrary(dir + path);
			for (String s : new File(dir + path).list()) {
				System.out.println(" |  found: " + s);
			}

		} catch (NullPointerException e) {
			Utility.error("Jar detected", "Core.setLibrary()");
			File file = null;
			for (String myLibName : libs) {
				InputStream inputStream = Core.class.getClassLoader()
						.getResourceAsStream(path + "/" + myLibName);

				try {
					file = new File(myLibName);
					if (!file.exists()) {
						file.createNewFile();
					}
					file.deleteOnExit();
					FileOutputStream fileOutputStream = new FileOutputStream(
							file);
					byte[] buffer = new byte[8192];
					int bytesRead;
					if (inputStream == null)
						System.out.println("NULL INPUT STREAM");
					while ((bytesRead = inputStream.read(buffer)) > 0) {
						fileOutputStream.write(buffer, 0, bytesRead);
					}
					fileOutputStream.close();
					inputStream.close();
				} catch (Exception ioe) {
					ioe.printStackTrace();
				}
			}
			assert file != null;
			String workingDir = file.getAbsoluteFile().getParent();
			for (String s : new File(workingDir).list()) {
				System.out.println(" |  found: " + s);
			}
			System.setProperty("org.lwjgl.librarypath", workingDir);
			System.setProperty("java.library.path", workingDir);
		}
	}

	public Container getContainer() {
		return container;
	}

	public void setSize(int width, int height) {
		if (internalWindow != null)
			internalWindow.setSize(width, height);
	}

	class InternalWindow extends AWTGLCanvas {

		/**
		 * generated VersionUID
		 */
		private static final long serialVersionUID = 1L;

		boolean toresize = false;

		public InternalWindow() throws LWJGLException {
			addComponentListener(new java.awt.event.ComponentAdapter() {
				public void componentResized(java.awt.event.ComponentEvent e) {
					Core.this.width = getWidth();
					Core.this.height = getHeight();
					toresize = true;
				}
			});
		}

		public void initGL() {
			StaticRef.camera.initGL();
			creation();
		}

		public void paintGL() {
			if (getSize().getHeight() == 0 || getSize().getWidth() == 0)
				return;
			// added the following line for the Desktop pane
			if (getWidth() != container.getWidth()
					|| getHeight() != container.getHeight()) {
				setSize(container.getWidth(), container.getHeight());
				toresize = true;
			}
			if (toresize) {
				toresize = false;
				Core.this.width = getWidth();
				Core.this.height = getHeight();
				StaticRef.getCamera().setDrawingArea(getWidth(), getHeight());
			}

			coreLoop();
			coreRender();
			try {
				swapBuffers();
			} catch (LWJGLException le) {
				le.printStackTrace();
			}

		}
	}

}
